﻿var AjaxHandler = new Interface("AjaxHandler", ["request", "createXhrObject"]);

// Simple Handler
var SimpleHandler = function () { };
SimpleHandler.prototype = {
    request: function (method, url, callback, params) {
        var xhr = this.createXhrObject();
        xhr.onreadystatechange = function () {
            if (xhr.readyState !== 4) return;
            (xhr.status === 200) ?
                callback.success(xhr.responseText, xhr.responseXML) :
                callback.failure(xhr.status);
        };
        xhr.open(method, url, true);
        if (method !== "POST") params = null;
        xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');
        xhr.send(params);
    },
    createXhrObject: function () {
        var methods = [
            function () { return new XMLHttpRequest(); },
            function () { return new ActiveXObject('Msxml2.XMLHTTP'); },
            function () { return new ActiveXObject('Microsoft.XMLHTTP'); },
        ];

        for (var i = 0, len = methods.length; i < len; i++) {
            try{
                methods[i]();
            } catch (e) {
                continue;
            }

            this.createXhrObject = methods[i];
            return methods[i]();
        }

        throw new Error("SimpleHandler: Could not create an XHR object.");
    }
}

// Queue Handler
var QueueHandler = function () {
    this.queue = [];
    this.requestInProgress = false;
    this.retryDelay = 5;// In seconds
};

extend(QueueHandler, SimpleHandler);
QueueHandler.prototype.request = function (method, url, callback, params, override) {
    if (this.requestInProgress && !override) {
        this.queue.push({ method: method, url: url, callback: callback, params: params });
    } else {
        this.requestInProgress = true;
        var xhr = this.createXhrObject(),
            _this = this;
        xhr.onreadystatechange = function () {
            if (xhr.readyState !== 4) return;
            if (xhr.status === 200) {
                callback.success(xhr.responseText, xhr.reponseXML);
                _this.advanceQueue();
            } else {
                callback.failure(xhr.status);
                setTimeout(function () { _this.request(method, url, callback, params); }, _this.retryDelay * 1000);
            }
        };
        xhr.open(method, url, true);
        if (method !== "POST") params = null;
        xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');
        xhr.send(params);
    }
};
QueueHandler.prototype.advanceQueue = function () {
    if (this.queue.length === 0) {
        this.requestInProgress = false;
        return;
    }

    var req = this.queue.shift();
    this.request(req.method, req.url, req.callback, req.params, true);
};

// Offline Handler
var OfflineHandler = function () {
    this.storedRequests = [];
};

OfflineHandler.extend(SimpleHandler);
OfflineHandler.prototype.request = function (method, url, callback, params) {
    if (XhrManager.isOffline()) {
        this.storedRequests.push({ method: method, url: url, callback: callback, params: params });
    } else {
        this.flushStoredRequests();
        OfflineHandler.superClass.request(method, url, callback, params);
    }
};
OfflineHandler.prototype.flushStoredRequests = function () {
    for (var i = 0, len = this.storedRequests.length; i < len; i++) {
        var req = this.storedRequests[i];
        OfflineHandler.superClass.request(req.method, req.url, req.callback, req.params, true);
    }
};

/* XhrManager singleton. */
var XhrManager = {
    createXhrHandler: function () {
        var xhr;
        if (this.isOffline()) {
            xhr = new OfflineHandler();
        } else if (this.isHighLatency()) {
            xhr = new QueueHandler();
        } else {
            xhr = new SimpleHandler();
        }

        Interface.ensureImplements(xhr, AjaxHandler);
        return xhr;
    },
    isOffline: function () { },
    isHighLatency: function () { }
};